package general

import (
	"alex/ptp1588boundaryclock/datasets"
	"alex/ptp1588boundaryclock/datatypes"
)

type AnnounceMessage struct {
	StepsRemoved uint16
	ParentDS *datasets.ParentDS
	TimePropertiesDS *datasets.TimePropertiesDS
	Timestamp *datatypes.TimeStamp
	SourcePortIdentity *datatypes.PortIdentity
}

func (a* AnnounceMessage) Init() {
	a.ParentDS = new(datasets.ParentDS)
	a.TimePropertiesDS = new(datasets.TimePropertiesDS)
	a.Timestamp = new(datatypes.TimeStamp)
	a.SourcePortIdentity = new(datatypes.PortIdentity)
}

// =====================================================================================================================
// Write Announce Message
// =====================================================================================================================

// Writes the Announce Message
func (a *AnnounceMessage) Write(announce []byte, done chan bool) {
	// originTimestamp (Timestamp) 13.5.2.1 -> no timestamp needed (7.3.3.2)
	// The value of originTimestamp shall be 0 or an estimate no worse than ±1 s of the local time of the originating
	// clock when the Announce message was transmitted.
	for i := 0; i < 10; i++ {
		announce[i] = 0
	}
	// currentUtcOffset
	announce[10], announce[11] = uint8(a.TimePropertiesDS.CurrentUtcOffset >> 8), uint8(a.TimePropertiesDS.CurrentUtcOffset)
	// reserved
	announce[12] = 0
	// grandmasterPriority1
	announce[13] = a.ParentDS.GrandmasterPriority1
	// grandmasterClockQualtiy
	a.setAnnounceGrandmasterClockQuality(announce[14:18])
	// grandmasterPriority2
	announce[18] = a.ParentDS.GrandmasterPriority2
	// grandmasterIdentity
	a.setAnnounceGrandmasterIdentity(announce[19:27])
	// stepsRemoved
	announce[27], announce[28] = uint8(a.StepsRemoved >> 8), uint8(a.StepsRemoved)
	// timeSource
	announce[29] = a.TimePropertiesDS.TimeSource
	done <- true
}

func (a* AnnounceMessage) setAnnounceGrandmasterClockQuality(quality []byte) {
	quality[0] = a.ParentDS.GrandmasterClockQuality.ClockClass
	quality[1] = a.ParentDS.GrandmasterClockQuality.ClockAccuracy
	quality[2] = uint8(a.ParentDS.GrandmasterClockQuality.OffsetScaledVarianceLog >> 8)
	quality[3] = uint8(a.ParentDS.GrandmasterClockQuality.OffsetScaledVarianceLog)
}

func (a* AnnounceMessage) setAnnounceGrandmasterIdentity(identity []byte) {
	for key, value := range a.ParentDS.GrandmasterIdentity {
		identity[key] = value
	}
}

// =====================================================================================================================
// Read Announce Message
// =====================================================================================================================

// Reads, checks and sets the Announce Message values
func (a* AnnounceMessage) Read(announce []byte, read uint16) (check bool) {
	check = true
	// originTimestamp 13.5.2.1
	a.readTimestamp(announce[:10])
	// currentUtcOffset 13.5.2.2
	a.TimePropertiesDS.CurrentUtcOffset = int16(announce[10]) << 8 | int16(announce[11])
	// reserved
	if announce[12] != 0 {
		check = false
	}
	// grandmasterPriority1 13.5.2.3
	a.ParentDS.GrandmasterPriority1 = announce[13]
	// grandmasterClockQuality 13.5.2.4
	if class, acc := a.readGrandmasterClockQuality(announce[14:18]); !class || !acc {
		check = false
	}
	// grandmasterPriority2 13.5.2.5
	a.ParentDS.GrandmasterPriority2 = announce[18]
	// grandmasterIdentity 13.5.2.6
	a.readGrandmasterIdentity(announce[19:27])
	// stepsRemoved 13.5.2.7
	a.StepsRemoved = uint16(announce[27]) << 8 | uint16(announce[28])
	// timeSource 13.5.2.8

	return
}

// Reads and sets the originTimestamp 13.5.2.1
func (a* AnnounceMessage) readTimestamp(timestamp []byte) {
	// we use uint64 since uint48 is not available in Go
	a.Timestamp.SecondsField = uint64(timestamp[0]) << 40 | uint64(timestamp[1]) << 32 | uint64(timestamp[2]) <<
			24 | uint64(timestamp[3]) << 16 | uint64(timestamp[4]) << 8 | uint64(timestamp[5])
	a.Timestamp.NanosecondsField = uint32(timestamp[6]) << 24 | uint32(timestamp[7]) << 16 | uint32(timestamp[8]) <<
			8 | uint32(timestamp[9])
}

// Sets and checks the GrandmasterClockQuality
func (a* AnnounceMessage) readGrandmasterClockQuality(quality []byte) (checkClass, checkAcc bool) {
	// Checks if ClockQuality is valid
	if quality[0] == 6 || quality[0] == 7 || quality[0] == 13 || quality[0] == 14 || quality[0] == 52 ||
		quality[0] == 58 || quality[0] == 187 || quality[0] == 193 || quality[0] == 248 || quality[0] == 255 {
		checkClass = true
	}
	if !checkClass {
		checkClass = a.readQualityHelper(uint8(68), uint8(122), quality[0])
	}
	if !checkClass {
		checkClass = a.readQualityHelper(uint8(133), uint8(170), quality[0])
	}
	if !checkClass {
		checkClass = a.readQualityHelper(uint8(216), uint8(232), quality[0])
	}
	if checkClass {
		a.ParentDS.GrandmasterClockQuality.ClockClass = quality[0]
		// Checks if ClockAccuracy is valid
		checkAcc = a.readQualityHelper(uint8(0x20), uint8(0x31), quality[1])
		if !checkAcc {
			checkAcc = a.readQualityHelper(uint8(0x80), uint8(0xFE), quality[1])
		}
		if checkAcc {
			a.ParentDS.GrandmasterClockQuality.ClockAccuracy = quality[1]
			a.ParentDS.GrandmasterClockQuality.OffsetScaledVarianceLog = uint16(quality[2]) << 8 | uint16(quality[2])
		}
	}
	return
}

// Reads and sets the GrandmasterIdentity -> no checking here.... 13.5.2.6
func (a* AnnounceMessage) readGrandmasterIdentity(identity []byte) {
	for key, value := range identity {
		a.ParentDS.GrandmasterIdentity[key] = value
	}
}

// Reads timeSource 13.5.2.8
func (a* AnnounceMessage) readTimeSource(src byte) (check bool) {
	if src == 0x20 || src == 0x30 || src == 0x40 || src == 0x50 || src == 0x60 || src == 0x90 || src == 0xA0 {
		check = true
	}
	return
}

// Helper function for GrandmasterClockQuality
func (a* AnnounceMessage) readQualityHelper(start, end uint8, value byte) (check bool) {
	for ;start < end+1 && !check; start++ {
		if value == start {
			check = true
		}
	}
	return
}

// =====================================================================================================================
// Implementing BMCDataset Interface
// =====================================================================================================================
func (a* AnnounceMessage) GetPriority1() uint8 {
	return a.ParentDS.GrandmasterPriority1
}

func (a* AnnounceMessage) GetIdentity() *datatypes.ClockIdentity {
	return &a.ParentDS.GrandmasterIdentity
}

func (a* AnnounceMessage) GetQuality() *datatypes.ClockQuality {
	return &a.ParentDS.GrandmasterClockQuality
}

func (a* AnnounceMessage) GetPriority2() uint8 {
	return a.ParentDS.GrandmasterPriority2
}

func (a* AnnounceMessage) GetStepsRemoved() uint16 {
	return a.StepsRemoved
}

func (a* AnnounceMessage) GetIdentityOfSender() *datatypes.ClockIdentity {
	return &a.SourcePortIdentity.ClockIdentity
}

func (a* AnnounceMessage) GetIdentityOfReceiver() *datatypes.ClockIdentity {
	return &a.ParentDS.ParentPortIdentity.ClockIdentity
}

func (a* AnnounceMessage) GetPortNumberOfReceiver() uint16 {
	return a.ParentDS.ParentPortIdentity.PortNumber
}

